package ua.lviv.franko.solvers.stressanalysis.oneDimention;

import java.util.List;

import ua.lviv.franko.integration.GaussQuadrature;
import ua.lviv.franko.integration.IFunction1D;
import ua.lviv.franko.integration.Quadrature;

public abstract class AbstractElement1D {

	public static int				QuadraturePointsNumber	= 19;
	protected Quadrature			gau;
	// problem data
	protected EquationParameters	eParams;
	protected double				kKk1;
	protected double				kKk2;
	protected double				kKk3;
	protected IFunction1D			rho1;
	protected IFunction1D			rho3;
	protected IFunction1D			mu1;
	// element number starting from 0
	protected int					elNumber;
	// element information
	protected final double			left;
	protected final double			right;
	protected List<IFunction1D>		baseFunctions;

	// results
	protected ElementMatrix			data;

	public AbstractElement1D(final EquationParameters eParams, int elNumber, final double left, final double right) {
		super();
		gau = new GaussQuadrature(QuadraturePointsNumber);
		this.eParams = eParams;
		this.elNumber = elNumber;
		this.left = left;
		this.right = right;

		this.eParams = eParams;
		kKk1 = eParams.getE() * eParams.getH() / (1 - eParams.getV() * eParams.getV());
		kKk2 = eParams.getkPrime() * eParams.getG() * eParams.getH();
		kKk3 = eParams.getE() * Math.pow(eParams.getH(), 3) / (12 * (1 - eParams.getV() * eParams.getV()));

		rho1 = new IFunction1D() {

			@Override
			public double calculate(final double xxx) {
				double h = eParams.getH();
				final double k1 = eParams.getK1().calculate(xxx);

				return gau.integrate(-h / 2, h / 2, new IFunction1D() {

					@Override
					public double calculate(double x) {
						double f1 = eParams.getF1().calculate(xxx, x);
						return (1 + k1 * x) * f1;
					}

					@Override
					public double calculateDerivaty(double x) {

						return 0;
					}
				});
			}

			@Override
			public double calculateDerivaty(double xxx) {

				return 0;
			}
		};

		rho3 = new IFunction1D() {

			@Override
			public double calculate(final double xxx) {
				double h = eParams.getH();
				final double k1 = eParams.getK1().calculate(xxx);

				return gau.integrate(-h / 2, h / 2, new IFunction1D() {

					@Override
					public double calculate(double x) {
						double f1 = eParams.getF3().calculate(xxx, x);
						return (1 + k1 * x) * f1;
					}

					@Override
					public double calculateDerivaty(double x) {

						return 0;
					}
				});
			}

			@Override
			public double calculateDerivaty(double xxx) {

				return 0;
			}
		};

		mu1 = new IFunction1D() {

			@Override
			public double calculate(final double xxx) {
				double h = eParams.getH();
				final double k1 = eParams.getK1().calculate(xxx);

				return gau.integrate(-h / 2, h / 2, new IFunction1D() {

					@Override
					public double calculate(double x) {
						double f1 = eParams.getF1().calculate(xxx, x);
						return (1 + k1 * x) * x * f1;
					}

					@Override
					public double calculateDerivaty(double x) {

						return 0;
					}
				});
			}

			@Override
			public double calculateDerivaty(double xxx) {

				return 0;
			}
		};

	}

	// protected abstract double[][] calculateSmallMatrix(int i, int j);
	//
	// protected abstract double[] calculateSmallVector(int i);

	public abstract double[] calculate(double x, double[] results);

	public ElementMatrix generateAll() {
		ElementMatrix data = new ElementMatrix(this.getNodesNumbers());
		int N = baseFunctions.size();
		for (int i = 0; i < N; i++) {
			ElementMatrix tmp = this.calculateSmallData(i, i);

			data.vector[3 * i] = tmp.vector[0];
			data.vector[3 * i + 1] = tmp.vector[1];
			data.vector[3 * i + 2] = tmp.vector[2];

			for (int j = 0; j < N; j++) {
				tmp = this.calculateSmallData(i, j);

				data.matrix[i * 3][j * 3] = tmp.matrix[0][0];
				data.matrix[i * 3][j * 3 + 1] = tmp.matrix[0][1];
				data.matrix[i * 3][j * 3 + 2] = tmp.matrix[0][2];

				data.matrix[i * 3 + 1][j * 3] = tmp.matrix[1][0];
				data.matrix[i * 3 + 1][j * 3 + 1] = tmp.matrix[1][1];
				data.matrix[i * 3 + 1][j * 3 + 2] = tmp.matrix[1][2];

				data.matrix[i * 3 + 2][j * 3] = tmp.matrix[2][0];
				data.matrix[i * 3 + 2][j * 3 + 1] = tmp.matrix[2][1];
				data.matrix[i * 3 + 2][j * 3 + 2] = tmp.matrix[2][2];
			}

		}

		return data;
	}
	
	public ElementMatrix generateUpdatedVector(EquationParameters parameters){
		ElementMatrix data = new ElementMatrix(this.getNodesNumbers());
		int N = baseFunctions.size();
		for (int i = 0; i < N; i++) {
			double[] tmp = this.calculateSmallUpdatedVector(i, parameters);

			data.vector[3 * i] = tmp[0];
			data.vector[3 * i + 1] = tmp[1];
			data.vector[3 * i + 2] = tmp[2];
		}

		return data;
	};
	
	protected abstract double[] calculateSmallUpdatedVector(int i,EquationParameters parameters);

	protected abstract ElementMatrix calculateSmallData(int i, int j);

	public abstract List<Integer> getNodesNumbers();

	public List<IFunction1D> getBaseFunctions() {
		return baseFunctions;
	}

	public double getLeft() {
		return left;
	}

	public double getRight() {
		return right;
	}
	
	public EquationParameters getEquationParameters() {
		return eParams;
	}

}
